package org.llc.authorization.config;

import org.llc.authorization.config.util.AuthProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.llc.authorization.service.MyBatisClientDetailsServiceImpl;
import org.llc.authorization.service.MyJdbcAuthorizationCodeServices;
import org.llc.authorization.service.RedisAuthorizationCodeServices;
import org.llc.authorization.service.UserLoginServiceImpl;
import org.llc.common.starter.handle.AuthExceptionEntryPoint;
import org.llc.common.starter.handle.Oauth2WebResponseExceptionTranslator;

import javax.sql.DataSource;

/**
 * Oauth2.授权服务配置
 *
 * @author llc
 * @date 2020/1/8 10:11
 * @since 1.0.0
 */
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 认证管理器
     */
    private final AuthenticationManager authenticationManager;

    /**
     * 客户端业务操作
     */
    private final MyBatisClientDetailsServiceImpl myBatisClientDetailsServiceImpl;

    /**
     * 数据源
     */
    private final DataSource dataSource;

    /**
     * 查询认证用户信息
     */
    private final UserLoginServiceImpl userLoginServiceImpl;

    /**
     * tokenStore
     */
    private final TokenStore tokenStore;

    /**
     * 自定义token生成
     */
    private final JwtAccessTokenConverter jwtAccessTokenConverter;

    /**
     * 自定义token增强器
     */
    private final TokenEnhancer tokenEnhancer;

    /**
     * 自定义异常翻译
     */
    private final Oauth2WebResponseExceptionTranslator oauth2WebResponseExceptionTranslator;

    /**
     * 用来解决匿名用户访问无权限资源时的异常
     */
    private final AuthExceptionEntryPoint authExceptionEntryPoint;

    /**
     * redis
     */
    private final RedisTemplate<String, Object> redisTemplate;

    public AuthServerConfig(DataSource dataSource,
                            AuthenticationManager authenticationManager,
                            MyBatisClientDetailsServiceImpl myBatisClientDetailsServiceImpl,
                            UserLoginServiceImpl userLoginServiceImpl,
                            TokenStore tokenStore,
                            @Autowired(required = false)
                            @Qualifier(value = "accessTokenConverter")
                                    JwtAccessTokenConverter jwtAccessTokenConverter,
                            RedisTemplate<String, Object> redisTemplate,
                            @Autowired(required = false)
                            @Qualifier(value = "customTokenEnhancer")
                                    TokenEnhancer tokenEnhancer,
                            Oauth2WebResponseExceptionTranslator oauth2WebResponseExceptionTranslator,
                            AuthExceptionEntryPoint authExceptionEntryPoint) {
        this.dataSource = dataSource;
        this.authenticationManager = authenticationManager;
        this.myBatisClientDetailsServiceImpl = myBatisClientDetailsServiceImpl;
        this.userLoginServiceImpl = userLoginServiceImpl;
        this.tokenStore = tokenStore;
        this.jwtAccessTokenConverter = jwtAccessTokenConverter;
        this.redisTemplate = redisTemplate;
        this.tokenEnhancer = tokenEnhancer;
        this.oauth2WebResponseExceptionTranslator = oauth2WebResponseExceptionTranslator;
        this.authExceptionEntryPoint = authExceptionEntryPoint;
    }

    /**
     * 配置客户端管理
     *
     * @param clients 客户端配置
     * @author llc
     * @date 2020/1/10 17:55
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(myBatisClientDetailsServiceImpl);
    }

    /**
     * 配置jdbc管理授权码
     *
     * @return org.springframework.security.oauth2.provider.code.AuthorizationCodeServices
     * @author llc
     * @date 2020/1/10 17:54
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new MyJdbcAuthorizationCodeServices(dataSource);
    }

    /**
     * 配置Redis管理授权码
     *
     * @return org.springframework.security.oauth2.provider.code.AuthorizationCodeServices
     * @author llc
     * @date 2020/1/17 16:38
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServicesRedis() {
        RedisAuthorizationCodeServices redisAuthorizationCodeServices = new RedisAuthorizationCodeServices();
        redisAuthorizationCodeServices.setRedisTemplate(redisTemplate);
        return redisAuthorizationCodeServices;
    }

    /**
     * 配置token管理
     *
     * @param endpoints 认证中心配置
     * @author llc
     * @date 2020/1/10 17:52
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        // jwt方式
        if (tokenStore instanceof JwtTokenStore) {
            // 配置tokenStore
            endpoints.tokenStore(tokenStore)
                    // 设置自定义token转换器
                    .accessTokenConverter(jwtAccessTokenConverter);
        } else {
            // redis方式
            endpoints.tokenStore(tokenStore);
        }
        // 通过注入密码授权被打开AuthenticationManager
        endpoints.authenticationManager(authenticationManager)
                // 该字段设置设置refresh token是否重复使用,true:reuse;false:no reuse.
                // 目前jwt方式设置这个参数好像没有用
                .reuseRefreshTokens(false)
                // 刷新令牌授权将包含对用户详细信息的检查，以确保该帐户仍然活动,因此需要配置userDetailsService
                .userDetailsService(userLoginServiceImpl)
                // 配置管理授权码
                .authorizationCodeServices(authorizationCodeServicesRedis());
        // 最后一个参数为替换之后页面的url
        endpoints.pathMapping("/oauth/confirm_access", AuthProperties.CONFIRM_ACCESS);
        // 处理 ExceptionTranslationFilter 抛出的异常
        endpoints.exceptionTranslator(oauth2WebResponseExceptionTranslator);
    }

    /**
     * 对应于配置AuthorizationServer安全认证的相关信息，创建ClientCredentialsTokenEndpointFilter核心过滤器
     *
     * @param security 定义令牌端点上的安全约束。配置token获取合验证时的策略
     * @author llc
     * @date 2020/1/10 17:52
     **/
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        // 允许所有人请求令牌
        security.tokenKeyAccess("permitAll()")
                // 已验证的客户端才能请求check_token端点
                .checkTokenAccess("isAuthenticated()")
                // 自定义异常处理端口
                .authenticationEntryPoint(authExceptionEntryPoint)
                // 允许表单登录
                .allowFormAuthenticationForClients();
    }
}
